package com.cropdoctor.service.impl;

import com.alibaba.fastjson.JSON;
import com.aliyun.oss.OSS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cropdoctor.common.CustomException;
import com.cropdoctor.common.EsMsgHandler;
import com.cropdoctor.common.Result;
import com.cropdoctor.mapper.CategoryMapper;
import com.cropdoctor.mapper.VegeFruTableMapper;
import com.cropdoctor.model.constants.PageParams;
import com.cropdoctor.model.constants.SearchConstants;
import com.cropdoctor.model.dto.AddOrUpdateVegeFrutableDto;
import com.cropdoctor.model.dto.VegeFrutableDto;
import com.cropdoctor.model.po.CategoryPo;
import com.cropdoctor.model.po.VegeFrutablePo;
import com.cropdoctor.model.vo.VegeFrutableVO;
import com.cropdoctor.service.VegeFruTableService;
import com.cropdoctor.util.AliOssUtil;
import io.redisearch.AggregationResult;
import io.redisearch.Document;
import io.redisearch.Query;
import io.redisearch.SearchResult;
import io.redisearch.aggregation.AggregationBuilder;
import io.redisearch.aggregation.SortedField;
import io.redisearch.aggregation.reducers.Reducers;
import io.redisearch.client.Client;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class VegeFruTableServiceImpl implements VegeFruTableService {
    @Autowired
    VegeFruTableMapper vegeFruTableMapper;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    CategoryMapper categoryMapper;

    @Autowired
    AliOssUtil ossUtil;

    @Autowired
    OSS ossClient;

    private static final String TABLE_KEY = "table";

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    @Qualifier(SearchConstants.TABLE_REDIS_SEARCH)
    Client redisSearchClient;

    public VegeFrutableVO getALL(String key,PageParams pageParams){
        long start = (pageParams.getPageNo() - 1) * pageParams.getPageSize();//起始位置
        Long total = redisTemplate.opsForList().size(key); //此处获得是所有的数据
        if(total == 0) {//缓存中无数据 从数据库中更新到缓存上
            List<VegeFrutablePo> list = vegeFruTableMapper.selectList(null);
            total = Long.valueOf(list.size());//数据总量
            if(total != 0) redisTemplate.opsForList().rightPushAll(key,list);//存入缓存
            else new VegeFrutableVO(new ArrayList<>(),total);
        }
        //封装返回结果类
        long end = total > start + pageParams.getPageSize() ? start + pageParams.getPageSize() - 1 : total - 1;
        List<VegeFrutablePo> pos = redisTemplate.opsForList().range(key, start, end);

        //转换结果
        List<VegeFrutableDto> list = pos.stream().map(item -> {
            VegeFrutableDto vegeFrutableDto = new VegeFrutableDto();
            BeanUtils.copyProperties(item, vegeFrutableDto);
            return vegeFrutableDto;
        }).collect(Collectors.toList());

        return new VegeFrutableVO(list,total);
    }

    public VegeFrutableVO getWithKeyword(String keyword, String mt, String st, PageParams pageParams) {
        StringBuilder q = new StringBuilder();
        q.append(keyword+" ");

        if (!"".equals(mt)) {
            q.append("@mt:"+"{"+mt+"} ");
        }
        if (!"".equals(st)) {
            q.append("@st:"+"{"+st+"}");
        }
        Query query = new Query(q.toString());

        int offset = (int) ((pageParams.getPageNo() - 1) * pageParams.getPageSize());
        int num = pageParams.getPageSize().intValue();
        query.limit(offset,num);

        SearchResult search = redisSearchClient.search(query);
        List<Document> docs = search.docs;
        List<VegeFrutableDto> list = new ArrayList<>();
        for (Document doc : docs) {
            VegeFrutableDto vegeFrutableDto = VegeFrutableDto.builder()
                    .id(Long.valueOf(doc.getString("id")))
                    .alias(doc.getString("alias"))
                    .name(doc.getString("name"))
                    .mt(doc.getString("mt"))
                    .url(doc.getString("url"))
                    .st(doc.getString("st"))
                    .build();
            list.add(vegeFrutableDto);
        }

        return new VegeFrutableVO(list, search.totalResults);
    }


    public VegeFrutableVO getByCategory(String mt,String st,PageParams pageParams){
        long start = (pageParams.getPageNo() - 1) * pageParams.getPageSize();//起始位置
        Long total = 0L;
        String key = !"".equals(mt) ? mt : st;
        total = redisTemplate.opsForList().size(key);//查询果蔬表总体数据量

        if(total == 0){
            //缓存中并没有数据
            LambdaQueryWrapper<VegeFrutablePo> wrapper = new LambdaQueryWrapper<>();
            wrapper
                    .eq(!"".equals(mt), VegeFrutablePo::getMt,mt)
                    .eq(!"".equals(st), VegeFrutablePo::getSt,st);
            List<VegeFrutablePo> pos = vegeFruTableMapper.selectList(wrapper);
            total = Long.valueOf(pos.size());//数据总量

            if(total == 0) {
                redisTemplate.opsForList().rightPushAll(key,Collections.singletonList("NULL"));//无论有没有值都存入缓存 防止缓存穿透
                redisTemplate.expire(key,20, TimeUnit.SECONDS);//设置过期时间
            }else{
                redisTemplate.opsForList().rightPushAll(key,pos);//无论有没有值都存入缓存 防止缓存穿透
            }
        }
        long end = total > start + pageParams.getPageSize() ? start + pageParams.getPageSize() - 1 : total - 1;
        List<VegeFrutablePo> pos = redisTemplate.opsForList().range(key, start, end);
        if("NULL".equals(pos.get(0))) return new VegeFrutableVO(Collections.emptyList(),0L);

        List<VegeFrutableDto> list = pos.stream().map(item -> {
            VegeFrutableDto vegeFrutableDto = new VegeFrutableDto();
            BeanUtils.copyProperties(item, vegeFrutableDto);
            return vegeFrutableDto;
        }).collect(Collectors.toList());

        return new VegeFrutableVO(list,total);
    }


    private VegeFrutableVO select(String mt, String st,String keyword,PageParams pageParams){
        if("".equals(keyword)){
            if(!"".equals(mt)|| !"".equals(st)){//仅分类参与搜索数据
                return getByCategory(mt,st,pageParams);
            }else{
                //需要全部数据
                return getALL(TABLE_KEY, pageParams);
            }
        }else{//有关键词 缓存方案不适用
            return getWithKeyword(keyword,mt,st,pageParams);
        }
    }

    @Override
    public Result<VegeFrutableVO> getVegeFruList(String mt, String st, String keyword, PageParams pageParams) {
        return Result.success(select(mt, st,keyword,pageParams));
    }


    @Override
    public Result<String> addOrUpdateVegeFru(AddOrUpdateVegeFrutableDto addOrUpdateVegeFrutableDto) {
        String url = null;
        MultipartFile file = addOrUpdateVegeFrutableDto.getImg();
        if(file != null){
            //新图片
            try {
                //获取原始的文件名
                String originalFilename = file.getOriginalFilename();
                //在oss中存储名字就是UUID + 文件的后缀名
                String objectName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
                url = ossUtil.upload(file.getBytes(), "veg_fru_img/"+objectName);//上传新图片
                String oldImg = addOrUpdateVegeFrutableDto.getOldImg();
                if(!"".equals(oldImg)) {
                    //如果有旧图片则需要删除
                    String[] split = oldImg.split("/");
                    objectName = split[split.length - 2] + "/" + split[split.length - 1];
                    ossClient.deleteObject("crop-doctor",objectName);//删除旧的图片
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new CustomException("上传图片失败!");
            }
        }else{
            //未上传新图片
            url = addOrUpdateVegeFrutableDto.getOldImg();
        }

        VegeFrutablePo vegeFrutablePo = new VegeFrutablePo();
        BeanUtils.copyProperties(addOrUpdateVegeFrutableDto,vegeFrutablePo);
        vegeFrutablePo.setUrl(url);//设置上图片地址
        int result;
        if(addOrUpdateVegeFrutableDto.getId() == 0){
            result = vegeFruTableMapper.insert(vegeFrutablePo);
            if(result > 0){
                //更新果蔬表缓存 并且将最新数据存入缓存
                addToRefreshTableCache(vegeFrutablePo);
                //更新对应分类缓存
                addToRefreshMtOrStCache(vegeFrutablePo.getMt(), vegeFrutablePo.getSt(),vegeFrutablePo);
            }
        }else{
            result = vegeFruTableMapper.updateById(vegeFrutablePo);
            if(result > 0){
                refreshTableCache();
                refreshMtOrStCache(vegeFrutablePo.getMt(), vegeFrutablePo.getSt());
            }
        }
        //同步es索引库
        if(result > 0){
            HashMap<String, Object> map = new HashMap<>();
            map.put("id",vegeFrutablePo.getId());
            map.put("Po", JSON.toJSONString(vegeFrutablePo));
            map.put("type","1");//新增或修改索引操作
            map.put("sqlTable","fruit_vegetable");//告诉错误消息出问题的是哪一个表中的主键
            rabbitTemplate.convertAndSend(EsMsgHandler.EXCHANGE_TOPIC,EsMsgHandler.ROUTINGKEY_TABLE,map);
            return Result.success("添加/删除成功!");
        }
        return Result.error("添加/删除失败!");
    }

    @Override
    public Result<String> delVegeFru(long id) {
        VegeFrutablePo vegeFrutablePo = vegeFruTableMapper.selectById(id);
        try {
            String url = vegeFrutablePo.getUrl();
            String[] split = url.split("/");
            String objectName = split[split.length - 2] + "/" + split[split.length - 1];
            ossClient.deleteObject("crop-doctor",objectName);
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException("删除失败!");
        }
        int result = vegeFruTableMapper.deleteById(id);
        if(result > 0){
            HashMap<String, Object> map = new HashMap<>();
            map.put("id",id);
            map.put("type","0");//新增或修改索引操作
            map.put("sqlTable","fruit_vegetable");//告诉错误消息出问题的是哪一个表中的主键
            rabbitTemplate.convertAndSend(EsMsgHandler.EXCHANGE_TOPIC,EsMsgHandler.ROUTINGKEY_TABLE,map);
        }
        refreshTableCache();
        refreshMtOrStCache(vegeFrutablePo.getMt(), vegeFrutablePo.getSt());
        return result > 0 ? Result.success("删除成功!") : Result.error("删除失败!");
    }

    @Override
    public Result<VegeFrutablePo> getByVegeFruId(int idTable) {
        VegeFrutablePo vegeFrutablePo = vegeFruTableMapper.selectById(idTable);
        return Result.success(vegeFrutablePo);
    }


    @Override
    public Result<List<CategoryPo>> getCategories(String mt) {
        //先从缓存中取
        if("".equals(mt)) mt ="ALL";
        List<CategoryPo> list = (List<CategoryPo>) redisTemplate.opsForValue().get("cat_"+mt);
        if(list != null && list.size() != 0) return Result.success(list);

        LambdaQueryWrapper<CategoryPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(!"".equals(mt) && !"ALL".equals(mt),CategoryPo::getMt,mt);
        list = categoryMapper.selectList(wrapper);

        //存入缓存
        redisTemplate.opsForValue().set("cat_"+mt,list);
        return Result.success(list);
    }

    @Override
    public Result<String> delCategories(Long id) {
        int result = categoryMapper.deleteById(id);
        refreshCategoryCache();
        return result > 0 ? Result.success("删除成功!") : Result.error("删除分类失败!");
    }

    @Override
    public Result<String> editCategories(CategoryPo categoryPo) {
        int result = categoryMapper.updateById(categoryPo);
        refreshCategoryCache();
        return result > 0 ? Result.success("编辑成功!") : Result.error("编辑分类失败!");
    }

    @Override
    public Result<String> addCategories(CategoryPo categoryPo) {
        int result = categoryMapper.insert(categoryPo);
        refreshCategoryCache();
        return result > 0 ? Result.success("添加成功!") : Result.error("添加分类失败!");
    }

    public void refreshCategoryCache(){
        Set<String> keys = redisTemplate.keys("cat_*");//批量删除以cat_为前缀的key
        redisTemplate.delete(keys);
    }

    public void refreshTableCache(){
        redisTemplate.delete(TABLE_KEY);
        List<VegeFrutablePo> list = vegeFruTableMapper.selectList(null);
        redisTemplate.opsForList().rightPushAll(TABLE_KEY,list);
    }

    //新增缓存
    public void addToRefreshTableCache(VegeFrutablePo vegeFrutablePo){
        redisTemplate.opsForList().rightPush(TABLE_KEY,vegeFrutablePo);
    }

    //新增缓存
    public void addToRefreshMtOrStCache(String mt,String st,VegeFrutablePo vegeFrutablePo){
        if(!"".equals(mt)) {
            redisTemplate.opsForList().rightPush(mt,vegeFrutablePo);
        }
        if(!"".equals(st)) {
            redisTemplate.opsForList().rightPush(st,vegeFrutablePo);
        }
    }

    public void refreshMtOrStCache(String mt,String st){
        LambdaQueryWrapper<VegeFrutablePo> wrapper = new LambdaQueryWrapper<>();
        if(!"".equals(mt)) {
            redisTemplate.delete(mt);
            wrapper.eq(VegeFrutablePo::getMt,mt);
            List<VegeFrutablePo> list = vegeFruTableMapper.selectList(wrapper);
            if(list != null) redisTemplate.opsForList().rightPushAll(mt,list);
        }
        if(!"".equals(st)) {
            redisTemplate.delete(st+"_cat");
            wrapper.eq(VegeFrutablePo::getSt,st);
            List<VegeFrutablePo> list = vegeFruTableMapper.selectList(wrapper);
            if(list != null) redisTemplate.opsForList().rightPushAll(st,list);
        }
    }
}
